﻿using HtmlAgilityPack;
using Newtonsoft.Json;
using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace NginxDlToAria2
{
    class Program
    {
        static void Main(string[] args)
        {
            //args = new string[] { "https://dl.lzhsb.cc/", "2", "http://192.168.50.1:6800/jsonrpc", "", "1", "https://lzhsb.cc/?deldl" };

            if (args.Length < 4)
            {
                Console.WriteLine("args: nginxUrl maxDeep aria2url aria2token [WaitFiniash:1/0] [FinishAction:url/exec] [url/cmd] [cmdArgs]");
            }
            else
            {
                getUrls(args[0], int.Parse(args[1]), args[2], args[3]);
                if (args.Length >= 5 && args[4] == "1")
                {
                    var res = waitfinish(args[2], args[3]);
                    purgeDownloadResult(args[2], args[3]);
                    if (args.Length >= 6 && res)
                    {
                        if (args[5] == "url")
                            callUrl(args[6]);
                        else
                            ExecCmd(args[6], args.Length >= 8 ? args[7] : null);
                    }
                }
            }

            //getUrls("http://az.lzhsb.cc/",3, "https://aria2.lzhsb.cc/jsonrpc","wdmmydybjd");
            // waitfinish("http://192.168.50.1:6800/jsonrpc", "");
            //purgeDownloadResult("http://192.168.50.1:6800/jsonrpc", "");
        }

        static void ExecCmd(string cmd,string arg)
        {
            var psi = new ProcessStartInfo(cmd, arg) { RedirectStandardOutput = true };
            var proc = Process.Start(psi);
            if (proc == null)
            {
                Console.WriteLine("Can not exec.");
            }
            else
            {
                using (var sr = proc.StandardOutput)
                {
                    while (!sr.EndOfStream)
                    {
                        Console.WriteLine(sr.ReadLine());
                    }
                    if (!proc.HasExited)
                    {
                        proc.Kill();
                    }
                }
            }
        }

        static void callUrl(string url)
        {
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(new Uri(url));
            webRequest.Method = "GET";
            HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
            StreamReader php = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
            php.ReadToEnd();
        }
        static void getUrls(string url,int deep,string aria2server,string token)
        {
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(new Uri(url));
            webRequest.Method = "GET";
            HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
            StreamReader php = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
            string phpend = php.ReadToEnd();
            //Console.WriteLine(phpend);

            HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
            doc.LoadHtml(phpend);

            HtmlNodeCollection uiListNodes = doc.DocumentNode.SelectNodes("//a");
            for (int i = 1; i < uiListNodes.Count; i++)
            {
                var a = uiListNodes[i];
                var turl = a.Attributes["href"].Value;
                if (turl.EndsWith("/"))
                {
                    if (deep > 0)
                        getUrls(url + turl, deep - 1, aria2server, token);
                }
                else
                {
                    addDLTask(aria2server, token, Guid.NewGuid().ToString(), url + turl);
                }
            }
        }
        static void addDLTask(string server, string token,string id, string dlurl)
        {
            var data = @"[{""jsonrpc"":""2.0"",""method"":""aria2.addUri"",""id"":"""+id+@""",""params""
:[""token:"+token+@""",["""+dlurl+@"""],
{""split"":""5"",""max-connection-per-server"":""16"",""seed-ratio"":""1.0""}]}]";
            //{""out"":"""+ savename + @""",""split"":""5"",""max-connection-per-server"":""16"",""seed-ratio"":""1.0""}]}]";
            byte[] byteArray = Encoding.UTF8.GetBytes(data);
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(new Uri(server));
            webRequest.Method = "POST";
            webRequest.ContentType = "application/json-rpc";
            webRequest.ContentLength = byteArray.Length;
            Stream newStream = webRequest.GetRequestStream();
            newStream.Write(byteArray, 0, byteArray.Length);
            newStream.Close();
            HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
            StreamReader php = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
            string phpend = php.ReadToEnd();
            Console.WriteLine(phpend);
            try
            {
                List<addResult> dt1 = JsonConvert.DeserializeObject<List<addResult>>(phpend);
                Tasks.Add(new fileResult()
                {
                    gid = dt1[0].result,
                    status = "waiting"
                });
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        static List<fileResult> Tasks = new List<fileResult>();
        static bool waitfinish(string server, string token)
        {
            while (Tasks.Any(f => f.status == "active"|| f.status == "waiting"))
            {
                Thread.Sleep(5000);
                checkStatus(server, token, "tellActive");
                checkStatus(server, token, "tellWaiting");
                checkStatus(server, token, "tellStopped");

                var active = Tasks.Count(f => f.status == "active");
                var waiting = Tasks.Count(f => f.status == "waiting");
                var paused = Tasks.Count(f => f.status == "paused");
                var complete = Tasks.Count(f => f.status == "complete");
                var removed = Tasks.Count(f => f.status == "removed");

                var totalLength = Tasks.Where(f=>f.status == "active" || f.status == "waiting").Sum(f => f.totalLength);
                var completedLength = Tasks.Where(f => f.status == "active" || f.status == "waiting").Sum(f => f.completedLength);
                var downloadSpeed = Tasks.Where(f => f.status == "active" || f.status == "waiting").Sum(f => f.downloadSpeed);

                if (active + waiting > 0)
                {
                    //Console.Clear();
                    Console.WriteLine("All:" + Tasks.Count + ", Active:" + active + ", Waiting:" + waiting + ", Paused:" + paused + " Complete:" + complete + ", Removed:" + removed);
                    Console.WriteLine(string.Format("Length:{0} / {1}", showlength(completedLength), showlength(totalLength)));
                    Console.WriteLine(string.Format("Speed:{0:N}", showlength(downloadSpeed)+"/s"));
                    if (downloadSpeed != 0)
                    {
                        var time = (totalLength - completedLength) / (downloadSpeed == 0 ? 1 : downloadSpeed);
                        var h = time / 3600;
                        var m = (time % 3600) / 60;
                        var s = time % 60;
                        Console.WriteLine(string.Format("Time:{0}h{1}m{2}s", h, m, s));
                    }
                    else
                    {
                        Console.WriteLine(string.Format("Time:N/A"));
                    }
                    Console.WriteLine(string.Format("*******************"));
                }
            }
            return Tasks.All(f => f.status == "complete");
        }


        static string showlength(int length)
        {
            length = length / 1024;
            var G = length / (1024 * 1024);
            var M = (length % (1024 * 1024)) / 1024;
            var k = length % 1024;
            if (G == 0 && M == 0)
            {
                return string.Format("{2}K", G, M, k);
            }
            if (G == 0)
            {
                return string.Format("{1}M {2}K", G, M, k);
            }

            return string.Format("{0}G {1}M", G, M, k);
        }

        static void checkStatus(string server, string token,string cmd)
        {
            var data = @"{""jsonrpc"":""2.0"",""method"":""aria2."+ cmd + @""",""id"":1,""params"":[""token:" + token + @""""+(cmd== "tellActive"?"": ",0,1000") +@"]}";
            byte[] byteArray = Encoding.UTF8.GetBytes(data);
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(new Uri(server));
            webRequest.Method = "POST";
            webRequest.ContentType = "application/json-rpc";
            webRequest.ContentLength = byteArray.Length;
            Stream newStream = webRequest.GetRequestStream();
            newStream.Write(byteArray, 0, byteArray.Length);
            newStream.Close();
            HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
            StreamReader php = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
            string phpend = php.ReadToEnd();
            downloadResult dt1 = JsonConvert.DeserializeObject<downloadResult>(phpend);
            foreach (var item in dt1.result)
            {
                var task = Tasks.FirstOrDefault(f => f.gid == item.gid);
                if (task != null)
                {
                    task.status = item.status;
                    task.totalLength = item.totalLength;
                    task.completedLength = item.completedLength;
                    task.downloadSpeed = item.downloadSpeed;
                    task.connections = item.connections;
                }
            }
        }

        static void purgeDownloadResult(string server, string token)
        {
            var data = @"{""jsonrpc"":""2.0"",""method"":""aria2.purgeDownloadResult"",""id"":1,""params"":[""token:" + token + @"""]}";
            byte[] byteArray = Encoding.UTF8.GetBytes(data);
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(new Uri(server));
            webRequest.Method = "POST";
            webRequest.ContentType = "application/json-rpc";
            webRequest.ContentLength = byteArray.Length;
            Stream newStream = webRequest.GetRequestStream();
            newStream.Write(byteArray, 0, byteArray.Length);
            newStream.Close();
            HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
            StreamReader php = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
            php.ReadToEnd();
        }

        class addResult
        {
            public string result { set; get; }
        }

        class downloadResult
        {
            public string id { set; get; }
            public List<fileResult> result { set; get; }
        }

        class fileResult
        {
            public string gid { set; get; }
            public int totalLength { set; get; }
            public int completedLength { set; get; }
            public int downloadSpeed { set; get; }
            public int connections { set; get; }
            public string status { set; get; }
        }

    }
}
